package com.fishingwithme.android.ui.screens.map.handlers

import android.util.Log
import android.widget.Toast
import com.fishingwithme.android.R
import com.fishingwithme.android.data.model.Position
import com.fishingwithme.android.utils.EventBus
import com.fishingwithme.android.utils.MapDataEvent
import com.tencent.tencentmap.mapsdk.maps.TencentMap
import com.tencent.tencentmap.mapsdk.maps.model.LatLng
import com.tencent.tencentmap.mapsdk.maps.model.MarkerOptions
import com.tencent.tencentmap.mapsdk.maps.model.Polygon
import com.tencent.tencentmap.mapsdk.maps.model.PolygonOptions
import com.tencent.tencentmap.mapsdk.maps.model.Polyline
import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions
import kotlin.math.abs

/**
 * 绘制多边形处理器
 */
class DrawPolygonEventHandler : BaseHandler() {
    override val key: String = "drawPolygon"
    override val icon: Int = R.mipmap.polygon
    override val showMenu: Boolean = true

    private var polyline: Polyline? = null
    private var polygon: Polygon? = null
    
    // Store the original click listeners to properly remove them later
    private var mapClickListener: TencentMap.OnMapClickListener? = null

    override fun doAction() {
        super.doAction()
        Log.d("DrawPolygonHandler", "Polygon drawing mode activated")
        setupEventListeners()
    }

    private fun setupEventListeners() {
        try {
            // Remove existing listeners if any
            removeListeners()
            
            // Set up click listener for adding points
            mapClickListener = com.tencent.tencentmap.mapsdk.maps.TencentMap.OnMapClickListener { latLng ->
                handleMapClick(latLng)
            }
            map?.setOnMapClickListener(mapClickListener)
        } catch (e: Exception) {
            Log.e("DrawPolygonHandler", "Error setting up event listeners", e)
        }
    }

    private fun handleMapClick(latLng: LatLng) {
        if (!isActive) return
        
        val point = Position(latLng.latitude, latLng.longitude)
        
        // If we already have a completed polygon, validate new points
        if (polygon != null && getPoints().size >= 3) {
            // Test adding the new point
            val testPoints = getPoints().toMutableList().apply { add(point) }
            
            // Check if the polygon would still be valid with this new point
            if (isValidPolygon(testPoints)) {
                // Add the point
                addPoint(point)
                // Update the polygon
                drawPolygon()
            } else {
                // Notify user the point is invalid
                context?.let {
                    Toast.makeText(it, "无法添加该点：会导致多边形无效", Toast.LENGTH_SHORT).show()
                }
                Log.d("DrawPolygonHandler", "Cannot add point: would cause invalid polygon")
            }
        } else {
            // Add point normally
            addPoint(point)
            
            // If we now have at least 3 points, draw the polygon
            if (getPoints().size >= 3) {
                drawPolygon()
            }
        }
    }

    private fun addPoint(point: Position) {
        // Add point to the data map
        val points = getPoints().toMutableList()
        points.add(point)
        data?.put("polygonPoints", points)
        
        addMarker(point)
        updatePolyline()
        
        Log.d("DrawPolygonHandler", "Added point ${points.size}: ${point.latitude}, ${point.longitude}")
    }

    private fun getPoints(): List<Position> {
        @Suppress("UNCHECKED_CAST")
        return data?.get("polygonPoints") as? List<Position> ?: emptyList()
    }

    private fun addMarker(point: Position) {
        try {
            val markerOptions = MarkerOptions(LatLng(point.latitude, point.longitude))
                .title("Point ${markers.size}")
            val marker = map?.addMarker(markerOptions)
            marker?.let {
                // Use BaseHandler's method to add marker to context
                addMarkerToMap(it)
            }
        } catch (e: Exception) {
            Log.e("DrawPolygonHandler", "Error adding marker", e)
        }
    }

    private fun updatePolyline() {
        try {
            // Remove existing polyline using BaseHandler's method
            polyline?.let { removePolylineFromMap(it) }
            
            // Create new polyline if we have at least 2 points
            if (getPoints().size > 1) {
                val path = getPoints().map { LatLng(it.latitude, it.longitude) }
                val polylineOptions = PolylineOptions()
                    .addAll(path)
                    .color(android.graphics.Color.RED)
                    .width(3f)
                
                polyline = map?.addPolyline(polylineOptions)
                polyline?.let { 
                    // Use BaseHandler's method to add polyline to context
                    addPolylineToMap(it) 
                }
            }
        } catch (e: Exception) {
            Log.e("DrawPolygonHandler", "Error updating polyline", e)
        }
    }

    private fun drawPolygon() {
        try {
            // Remove existing polygon using BaseHandler's method
            polygon?.let { removePolygonFromMap(it) }
            
            // Only draw if we have at least 3 points
            if (getPoints().size >= 3) {
                val path = getPoints().map { LatLng(it.latitude, it.longitude) }
                val polygonOptions = PolygonOptions()
                    .addAll(path)
                    .strokeColor(android.graphics.Color.RED)
                    .strokeWidth(3f)
                    .fillColor(android.graphics.Color.argb(100, 255, 0, 0)) // Semi-transparent red
                
                polygon = map?.addPolygon(polygonOptions)
                polygon?.let { 
                    // Use BaseHandler's method to add polygon to context
                    addPolygonToMap(it) 
                }
                
                Log.d("DrawPolygonHandler", "Polygon drawn with ${getPoints().size} points")
            }
        } catch (e: Exception) {
            Log.e("DrawPolygonHandler", "Error drawing polygon", e)
        }
    }

    /**
     * Check if a polygon defined by the given points is valid (no intersecting segments)
     * @param points List of positions defining the polygon vertices
     * @return true if the polygon is valid, false otherwise
     */
    fun isValidPolygon(points: List<Position>): Boolean {
        // A polygon needs at least 3 points
        if (points.size < 3) return false
        
        // Check for intersecting segments
        if (hasCrossingSegments(points)) {
            return false
        }
        
        // Check if closing segment would intersect with any other segments
        if (hasClosingCrossing(points)) {
            return false
        }
        
        return true
    }
    
    /**
     * Check if there are any intersecting segments in the polygon
     * @param points List of positions defining the polygon vertices
     * @return true if there are intersecting segments, false otherwise
     */
    private fun hasCrossingSegments(points: List<Position>): Boolean {
        if (points.size < 3) return false
        
        // Check all non-adjacent segments for intersection
        for (i in 0 until points.size - 2) {
            for (j in i + 2 until points.size - 1) {
                if (doSegmentsIntersect(points[i], points[i + 1], points[j], points[j + 1])) {
                    return true
                }
            }
        }
        
        return false
    }
    
    /**
     * Check if the closing segment (from last point to first point) intersects with any other segments
     * @param points List of positions defining the polygon vertices
     * @return true if the closing segment intersects with any other segments, false otherwise
     */
    private fun hasClosingCrossing(points: List<Position>): Boolean {
        if (points.size < 3) return false
        
        val lastPoint = points[points.size - 1]
        val firstPoint = points[0]
        
        // Check closing segment against all non-adjacent segments
        // Skip adjacent segments (0->1 and n-1->n)
        for (i in 1 until points.size - 2) {
            if (doSegmentsIntersect(lastPoint, firstPoint, points[i], points[i + 1])) {
                return true
            }
        }
        
        return false
    }
    
    /**
     * Check if two line segments intersect
     * @param p1 First point of first segment
     * @param p2 Second point of first segment
     * @param p3 First point of second segment
     * @param p4 Second point of second segment
     * @return true if the segments intersect, false otherwise
     */
    private fun doSegmentsIntersect(
        p1: Position, 
        p2: Position, 
        p3: Position, 
        p4: Position
    ): Boolean {
        val x1 = p1.longitude
        val y1 = p1.latitude
        val x2 = p2.longitude
        val y2 = p2.latitude
        val x3 = p3.longitude
        val y3 = p3.latitude
        val x4 = p4.longitude
        val y4 = p4.latitude
        
        // Calculate determinant
        val denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4)
        
        // If denominator is near zero, lines are parallel
        if (abs(denom) < 1e-10) return false
        
        val t = ((x1 - x3) * (y3 - y4) - (y1 - y3) * (x3 - x4)) / denom
        val u = -((x1 - x2) * (y1 - y3) - (y1 - y2) * (x1 - x3)) / denom
        
        // Segments intersect if both t and u are between 0 and 1
        return t >= 0 && t <= 1 && u >= 0 && u <= 1
    }
}